<template>
    <div class="table-container">
      <!-- 条件查询 -->
      <filter-form
        ref="filterFormRef"
        :search="search"
        :searchShowLen="searchShowLen"
        @handle-search="handleSearch" 
        @handle-reset="handleReset"
      />
      <!-- 标题栏 -->
      <div v-if="showTitleBar" class="table-toolbar">
        <!--顶部左侧区域-->
        <div class="table-toolbar-left">
          <template v-if="title">
            <div class="table-toolbar-left-title">
              {{ title }}
            </div>
          </template>
          <slot name="title"></slot>
        </div>
        <!--顶部右侧区域-->
        <div class="table-toolbar-right">
          <slot name="toolbar"></slot>
          <!-- 自定义列表操作按钮-->
          <el-button
            v-for="(v,index) in customBtnList"
            :key="index"
            :size="v.size || 'small'" 
            :icon="v.icon || '' " 
            :style="{background: v.bg || '#1890FF', color:  v.color || '#fff'}"
            @click="handleCustomBtn(v)">
            {{v.label}}
          </el-button>
      
          <!-- 筛选表头配置按钮-->
          <el-popover
            v-model:visible="filterColumnsVisible"
            placement="bottom-end"
            width="150"
            trigger="click">
            <el-checkbox-group v-model="filterColumnsList" class="flex flex-col">
              <el-checkbox
                v-for="(v,index) in saveFilterColumnsList"
                :key="index"
                :label="v.prop"
                class="mb-15"
              >{{ v.label }}
              </el-checkbox>
            </el-checkbox-group>
            <div class="flex aj-center">
              <el-button type="primary" size="mini" class="confirm" @click="handleFilterCol">确定</el-button>
              <el-button size="mini" @click="handleFilterColCancel">取消</el-button>
            </div>
            <template #reference>
              <el-button size="small" icon="el-icon-setting" style="background: #ecedf5;">配置</el-button>
            </template>
          </el-popover>
        </div>
      </div>
      <!-- 列表 -->
      <div class="table-content" ref="tableRef">
          <el-table
            style="width: 100%"
            :max-height="tableHeight"
            v-bind="$attrs"
            v-loading="loading"
            :data="tableData"
            :row-key="rowKey"
            :header-cell-style="headerCellStyle"
          >
          <!-- 无数据 -->
          <template #empty>
              <el-empty description="暂无数据"></el-empty>
          </template>
          <!-- 开启列表多选 -->
          <el-table-column
              v-if="showSelection"
              type="selection"
              :selectable="selectable"
              width="55">
          </el-table-column>
          <!-- 开启列表Index索引 -->
          <el-table-column
              v-if="showIndex"
              type="index"
              label="序号"
              :index="indexMethod"
              width="55">
          </el-table-column>
          <!-- 开启列表展开行-->
          <el-table-column v-if="showExpand" type="expand">
              <template #default="props">
                <slot name="expand" :row="props.row"></slot>
              </template>
          </el-table-column>
          <el-table-column
              v-for="(item,index) in tbHeaders"
              :key="item.prop || index"
              :label="item.label"
              :prop="item.prop"
              :width="item.width"
              :align="item.align"
              :sortable="item.sortable"
              :show-overflow-tooltip="!item.unShowTooltip"
            >
              <!-- 自定义列 -->
              <template #default="scope">
                <slot
                  :name='item.prop'
                  :row=" scope.row"
                  :col="item"
                  :prop="item.prop"
                >
                  <render-row
                    :row="scope.row"
                    :col="item"
                    @btn-click="handleBtnClick"
                    @command="handleCommand"
                  />
                </slot>
              </template>
            </el-table-column>
          </el-table>
      </div>
      <!-- 列表分页 -->
      <el-pagination
        class="pagination"
        v-if="paginationConfig.show && pagination.total > 0"
        v-model:currentPage="pagination.currentPage"
        v-model:pageSize="pagination.pageSize"
        :total="pagination.total"
        :page-sizes="paginationConfig.pageSizes"
        :layout="paginationConfig.layout"
        :style="paginationConfig.style"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>
</template>

<script lang="ts">
import { defineComponent, PropType, reactive, ref, toRefs, onMounted, nextTick, } from 'vue';
import { ColumnsType } from "./type";
import FilterForm from './FilterForm.vue';
import RenderRow from './RenderRow.vue';
// https://github.com/huzhushan/vue3-pro-table
export default defineComponent({
    name: "Table",
    inheritAttrs: false,
    components: {
      FilterForm,
      RenderRow,
    },
    emits: ['custom-click', 'btn-click', 'command'],
    props: {
      // 请求列表数据的方法
      request: {
        type: Function,
        default: () => ({
          data: [],
          total: 0,
        })
      },
      // 搜索表单配置，false表示不显示搜索表单
      search: {
        type: [Boolean, Object],
        default: true,
      },
      // 搜索区域收起时显示几个
      searchShowLen: {
        type: Number,
        default: 3,
      },
      title: {
        type: String,
        default: '列表标题'
      },
      // 是否显示标题栏
      showTitleBar: {
        type: Boolean,
        default: true
      },
      // 表头数据
      columns: {
        type: Array as PropType<ColumnsType>,
        default: () => []
      },
      // 自定义标题栏按钮
      customBtnList: {
        type: Array,
        default: () => []
      },
      // 分页配置，false表示不显示分页
      pagination: {
        type: [Boolean, Object],
        default: () => ({
          currentPage: 1,
          pageSize: 10,
          layout: "total, sizes, prev, pager, next, jumper",
          pageSizes: [10, 20, 30, 40, 50, 100],
          style: {}
        }),
      },
      // 是否可多选
      showSelection: {
        type: Boolean,
        default: false
      },
      // 仅对 type=selection 的列有效，类型为 Function，Function 的返回值用来决定这一行的 CheckBox 是否可以勾选
      selectable: {
        type: Function,
        default: (row: any, index: number) => true
      },
      // 表头行的 style 的回调方法或Object
      headerCellStyle: {
        type: [Object, Function],
        default: () => ({
          background: '#f7f8fa',
          color: '#323233',
        })
      },
      // 是否显示索引
      showIndex: {
        type: Boolean,
        default: false
      },
      // 自定义索引函数
      indexMethod: {
        type: Function,
        default: (index: number) => (index + 1)
      },
      // 是否展开行
      showExpand: {
        type: Boolean,
        default: false
      },
      // 渲染树形数据必填
      rowKey: {
        type: String,
        default: 'id'
      },
     
    },
    setup(props, { emit }) {
      const state: any = reactive({
        loading: false,
        tableData: [], // 列表数据
        pagination : {
          currentPage: 1,
          pageSize: (!!props.pagination && props.pagination.pageSize) || 10,
          total: 0,
        },
        paginationConfig: {
          show: false,
        },
        filterFormRef: ref<any[]>([]),
        tableRef: ref<any[]>([]),
        tableHeight: 0,
        // 筛选列弹窗可见
        filterColumnsVisible: false,
        // 筛选列弹窗选中项列表
        filterColumnsList: [],
        // 缓存初始表头,渲染筛选列弹出框
        saveFilterColumnsList: [],
        // 筛选后表头, 渲染表格表头
        tbHeaders: []
      });

      // 请求table数据
      const getTableData = async () => {
        state.loading = true;
        const searchModel = state.filterFormRef.optimizeFields(props.search);
        const { data, total } = await props.request({
          currentPage: state.pagination.currentPage,
          pageSize: state.pagination.pageSize,
          ...searchModel,
        });
        state.loading = false;
        state.tableData = data;
        state.pagination.total = total;
      };

      // 自定义列表顶部按钮点击事件
      const handleCustomBtn = (item) => {
        emit('custom-click',item);
      }

      // 确定筛选列
      const handleFilterCol = () => {
        state.filterColumnsVisible = false;
        // 过滤保留选中列
        state.tbHeaders = state.saveFilterColumnsList.filter((col: any) => state.filterColumnsList.includes(col.prop));
      };
      // 取消筛选列
      const handleFilterColCancel = () => {
        // 取消操作,还原筛选列弹窗选中项
        state.filterColumnsList = state.tbHeaders.map((v: any) => v.prop);
        state.filterColumnsVisible = false;
      };

      // 操作栏按钮点击事件
      const handleBtnClick = (detail: any) => {
        emit('btn-click', detail);
      }

      // 操作栏下拉菜单点击事件
      const handleCommand = (command: any) => {
        emit('command', command);
      }

      // 动态设置表格自适应高度
      const getAutoHeight = () => {
        nextTick(()=>{
          const rect = state.tableRef.getBoundingClientRect()
          state.tableHeight = rect.height
        })
      }

      // 当前页变化
      const handleCurrentChange = (page) => {
        getTableData()
      }
      // 改变每页size数量
      const handleSizeChange = (value) => {
        state.pagination.currentPage = 1
        getTableData()
      }

      // 刷新列表数据
      const refresh = () => {
        getTableData();
      }

      // 条件查询
      const handleSearch = () => {
        state.pagination.currentPage = 1;
        getTableData();
      }

      // 重置
      const handleReset = () => {
        state.pagination.currentPage = 1;
        getTableData();
      }

      if (typeof props.pagination === "object") {
        const { layout, pageSizes, style } = props.pagination;
        state.paginationConfig = {
          show: true,
          layout: layout || "total, sizes, prev, pager, next, jumper",
          pageSizes: pageSizes || [10, 20, 30, 40, 50, 100],
          style: style || {},
        };
      }

      state.saveFilterColumnsList = props.columns
      state.filterColumnsList = props.columns.map(v => v.prop)
      state.tbHeaders = props.columns

      onMounted(() => {
        getTableData()
        getAutoHeight()
        window.onresize = () => {
          getAutoHeight();
        };
      })

      return {
        ...toRefs(state),
        handleFilterCol,
        handleFilterColCancel,
        handleBtnClick,
        handleCommand,
        handleCustomBtn,
        handleCurrentChange,
        handleSizeChange,
        handleSearch,
        handleReset,
      }
    }
});
</script>

<style lang="scss" scoped>
.table-container{
  flex: auto;
  display: flex;
  flex-direction: column;
  .table-toolbar, .table-footer{
    flex-shrink: 0;
  }
  .table-content{
    flex: auto;
    margin: 10px 0;
  }
}

.table-toolbar{
  display: flex;
  justify-content: space-between;
  align-items: center;
  &-left {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    flex-shrink: 0;
    &-title {
      padding-left: 20px;
      display: flex;
      align-items: center;
      justify-content: flex-start;
      font-size: 16px;
      font-weight: 600;
    }
  }
  &-right {
    display: flex;
    justify-content: flex-end;
    flex-shrink: 0;

    &-icon {
      margin-left: 12px;
      font-size: 16px;
      cursor: pointer;
      color: var(--text-color);

      :hover {
        color: #1890ff;
      }
    }
  }
}

.pagination {
  padding: 0 20px;
  background: #fff;
  text-align: right;
  :last-child {
    margin-right: 0;
  }
}
.flex {
  display: flex;
}
.flex-col {
  flex-direction: column;
}
.mb-15 {
  margin-bottom: 15px;
}
:deep(.el-table__fixed-right-patch) {
  background-color: rgb(247, 248, 250) !important;
}
</style>
